import json
import os
from pathlib import Path
from typing import Any

ENV_XDG_CONFIG_HOME = "XDG_CONFIG_HOME"
DEFAULT_RELATIVE_XDG_CONFIG_HOME = Path(".config")
DEFAULT_CONFIG_DIRNAME = "cvek"


def get_default_config_dir():
    """Return the path to the cvek configuration directory."""
    home_dir = Path.home()
    xdg_config_home_dir = os.environ.get(
        ENV_XDG_CONFIG_HOME,
        home_dir / DEFAULT_RELATIVE_XDG_CONFIG_HOME,
    )
    return Path(xdg_config_home_dir) / DEFAULT_CONFIG_DIRNAME


DEFAULT_CONFIG_DIR = get_default_config_dir()


class ConfigFileError(Exception): ...


def read_raw_config(path: Path) -> dict[str, Any]:
    """
    Read and parse the raw configuration file as a JSON dictionary.

    Raises:
        FileNotFoundError: When the specified file does not exist
        ConfigFileError: When there is an OS-level error reading the file
    """
    try:
        with path.open("r") as f:
            try:
                return json.load(f)
            except ValueError as e:
                raise ConfigFileError(f"invalid file: {e} [{path}]")
    except FileNotFoundError:
        raise
    except OSError as e:
        raise ConfigFileError(f"cannot read {path} file: {e}")


class Config(dict):
    FILENAME = "config.json"
    DEFAULT = {}

    def __init__(self):
        super().__init__()
        self.update(self.DEFAULT)

        self.path = DEFAULT_CONFIG_DIR / self.FILENAME
        self.load_from_file()

    def load_from_file(self):
        try:
            if self.path.exists():
                if self.path.stat().st_size == 0:
                    config_data = {}
                else:
                    with open(self.path, 'r') as f:
                        config_data = json.load(f)
                self.update(config_data)  
        except Exception:
            os.remove(self.path)

    @property
    def gitlab_token(self):
        if "PRIVATE_TOKEN" in self:
            return self["PRIVATE_TOKEN"]
        
        try:
            self.load_from_file()
            return self["PRIVATE_TOKEN"]
        except Exception:
            return None

    def pre_process_data(self, data):
        # Remove PRIVATE_TOKEN if it's empty
        if not data.get("PRIVATE_TOKEN"):
            del data["PRIVATE_TOKEN"]

    def ensure_directory(self):
        self.path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)

    def load(self):
        data = read_raw_config(self.path)
        if data is not None:
            self.pre_process_data(data)
            self.update(data)

    def save(self):
        self.ensure_directory()
        json_str = json.dumps(obj=self, indent=4, sort_keys=True, ensure_ascii=True)
        self.path.write_text(json_str + "\n", encoding="utf-8")
